/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.form; import java.awt.*; import java.beans.*; import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.lang.reflect.Method; import javax.swing.JComponent; import org.openide.util.Utilities; import org.openide.util.io.*; import org.openide.explorer.propertysheet.editors.XMLPropertyEditor; import org.netbeans.modules.form.util.*; /** A class that contains utility methods for the formeditor. * * @author Ian Formanek */ public class FormUtils extends Object { // ----------------------------------------------------------------------------- // Static variables private static String PROP_NAME = "PropertyName"; // NOI18N private static final boolean debug = (System.getProperty ("netbeans.debug.form") != null); /** The list of all well-known heavyweight components */ private static Class[] heavyweightComponents; /** * @associates String */ private static HashMap jComponentIgnored; private static HashMap valuesCache = new HashMap (); /** The properties whose changes are ignored in JComponent subclasses */ private static String[] jComponentIgnoredList = new String [] { "UI", // NOI18N "layout", // NOI18N "actionMap", // NOI18N "border", // NOI18N "model" // NOI18N }; static { try { heavyweightComponents = new Class[] { java.awt.Button.class, java.awt.Canvas.class, java.awt.List.class, java.awt.Button.class, java.awt.Label.class, java.awt.TextField.class, java.awt.TextArea.class, java.awt.Checkbox.class, java.awt.Choice.class, java.awt.List.class, java.awt.Scrollbar.class, java.awt.ScrollPane.class, java.awt.Panel.class, }; } catch (Exception e) { throw new InternalError("Cannot initialize AWT classes"); // NOI18N } jComponentIgnored = new HashMap (15); for (int i = 0; i < jComponentIgnoredList.length; i++) jComponentIgnored.put (jComponentIgnoredList[i], jComponentIgnoredList[i]); } // ----------------------------------------------------------------------------- // Utility methods public static void notifyPropertyException (Class beanClass, String propertyName, String displayName, Throwable t, boolean reading) { boolean dontPrint = false; // if it is a subclass of Applet, we ignore InvocationTargetException // on codeBase, documentBase and appletContext properties if (java.applet.Applet.class.isAssignableFrom (beanClass)) if ("codeBase".equals (propertyName) || // NOI18N "documentBase".equals (propertyName) || // NOI18N "appletContext".equals (propertyName)) // NOI18N dontPrint = true; if ("tearOff".equals (propertyName) || "helpMenu".equals (propertyName)) // NOI18N dontPrint = true; if (!dontPrint) { // if (System.getProperty ("netbeans.full.hack") != null) // e.printStackTrace (); org.openide.TopManager.getDefault ().getStdOut ().println ( java.text.MessageFormat.format ( reading ? org.openide.util.NbBundle.getBundle (FormUtils.class).getString ("FMT_ERR_ReadingProperty") : org.openide.util.NbBundle.getBundle (FormUtils.class).getString ("FMT_ERR_WritingProperty"), new Object[] { t.getClass ().getName (), propertyName, displayName } ) ); } } /** A utility method that returns the string that should be used for indenting * the generated text. It is a String that is a tabSize of spaces */ public static String getIndentString () { return " "; // EditorSettingsJava.getIndentString (); // NOI18N } // ----------------------------------------------------------------------------- // JavaBeans helper mthods public static Object cloneObject (Object o) throws CloneNotSupportedException { if (o == null) return null; else if ((o instanceof Byte) || (o instanceof Short) || (o instanceof Integer) || (o instanceof Long) || (o instanceof Float) || (o instanceof Double) || (o instanceof Boolean) || (o instanceof Character) || (o instanceof String)) { return o; // no need to change reference } else if (o instanceof Font) return Font.getFont (((Font)o).getAttributes ()); else if (o instanceof Color) return new Color (((Color)o).getRGB ()); else if (o instanceof Dimension) return new Dimension ((Dimension)o); else if (o instanceof Point) return new Point ((Point)o); else if (o instanceof Rectangle) return new Rectangle ((Rectangle)o); else if (o instanceof Serializable) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (baos); oos.writeObject (o); oos.close (); ByteArrayInputStream bais = new ByteArrayInputStream (baos.toByteArray ()); ObjectInputStream ois = new ObjectInputStream (bais); return ois.readObject (); } catch (Exception e) { throw new CloneNotSupportedException (); } } throw new CloneNotSupportedException (); } /** A utility method for checking whether specified component is * heavyweight or lightweight. * @param comp The component to check */ public static boolean isHeavyweight(java.awt.Component comp) { for (int i=0; i < heavyweightComponents.length; i++) if (heavyweightComponents[i].isAssignableFrom(comp.getClass())) return true; return false; } public static boolean isIgnoredProperty (Class beanClass, String propertyName) { if (JComponent.class.isAssignableFrom (beanClass)) { if (jComponentIgnored.get (propertyName) != null) return true; } if (javax.swing.JDesktopPane.class.isAssignableFrom (beanClass) && "desktopManager".equals (propertyName)) // NOI18N return true; if (javax.swing.JInternalFrame.class.isAssignableFrom (beanClass) && "menuBar".equals (propertyName)) // NOI18N return true; return false; } /** @return a default name for event handling method - it is a concatenation of * the component name and the name of the listener method (with first letter capital) * (e.g. button1MouseReleased). */ public static String getDefaultEventName (RADComponent component, Method listenerMethod) { String componentName = component.getName (); if (component instanceof FormContainer) { componentName = "form"; // NOI18N } return getDefaultEventName (componentName, listenerMethod); } static String getDefaultEventName (String name, Method listenerMethod) { StringBuffer sb = new StringBuffer (name); String lm = listenerMethod.getName (); sb.append (lm.substring (0, 1).toUpperCase ()); sb.append (lm.substring (1)); return sb.toString (); } /** @return a formatted name of specified method */ public static String getMethodName (MethodDescriptor desc) { StringBuffer sb = new StringBuffer (desc.getName ()); Class[] params = desc.getMethod ().getParameterTypes (); if ((params == null) || (params.length == 0)) { sb.append (" ()"); // NOI18N } else { for (int i = 0; i < params.length; i++) { if (i == 0) sb.append (" ("); // NOI18N else sb.append (", "); // NOI18N sb.append (Utilities.getShortClassName (params[i])); } sb.append (")"); // NOI18N } return sb.toString (); } // ----------------------------------------------------------------------------- // Visual utility methods /** * This method is intended to be started from container's paint method to do all the * things concerning its grid. It is not necessary to use it, but it can hide some * low-level details to programmers who write their own containers and do not want to * go deep into implementation details. Moreover, by using this method it is sure that * the grid is done in a standard way. * @param xvc The container which wants to have a grid painted * @param g The Graphics givenas param. to paint method * @param gi Xvc's gridInfo * @param offsX x-offset to paint grid from * @param offsY y-offset to paint grid from * @param imW width of grid * @param imH height of grid */ public static void paintGrid(Component comp, Graphics g, GridInfo gi, int offsX, int offsY, int imW, int imH) { if (imW <= 0 || imH <=0 ) return; if (gi.getGridX() == 1 && gi.getGridY() == 1 ) return; // no grid if (gi.gridImage == null || gi.imWidth != imW || gi.imHeight != imH) new GridThread(comp, gi, imW, imH).run(); if (gi.gridImage != null) g.drawImage(gi.gridImage, offsX, offsY, null); } /** * This method converts array of Rectangles (with compoment bounds) to * GridBagConstraints. * * Some bug (properly in GridBagLayout) appeares: * * here will be bad size!!!! * | * V * mmmm mmmm mmmm mmmm * mmmmm */ public static GridBagConstraints[] convertToConstraints (Rectangle[] r, Component[] com) { int i, k = r.length; GridBagConstraints[] c = new GridBagConstraints[k]; for (i = 0; i < k; i++) { c [i] = new GridBagConstraints (); int gx = 0, x1 = r [i].x; int gy = 0, y1 = r [i].y; int gw = 1, x2 = x1 + r [i].width; int gh = 1, y2 = y1 + r [i].height; int fromX = 0, fromY = 0; int j, l = r.length; for (j = 0; j < l; j++) { int xe = r [j].x + r [j].width; int ye = r [j].y + r [j].height; if (xe <= x1) { gx++; fromX = Math.max (fromX, xe); } if (ye <= y1) { gy++; fromY = Math.max (fromY, ye); } if ((xe > x1) && (xe < x2)) gw++; if ((ye > y1) && (ye < y2)) gh++; } c [i].gridx = gx; c [i].gridy = gy; c [i].gridwidth = gw; c [i].gridheight = gh; c [i].insets = new Insets (y1 - fromY, x1 - fromX, 0, 0); c [i].fill = GridBagConstraints.BOTH; c [i].ipadx = (r [i].width - com [i].getPreferredSize ().width); c [i].ipady = (r [i].height - com [i].getPreferredSize ().height); } return c; } // ----------------------------------------------------------------------------- // DEBUG utilities public static void DEBUG () { if (debug) { Thread.dumpStack(); } } public static void DEBUG (String s) { if (debug) { System.out.println(s); } } /** A utility method which looks for the first common ancestor of the * classes specified. The ancestor is either one of the two classes, if one of them extends the other * or their first superclass which is common to both. * @param cl1 the first class * @param cl2 the first class * @return class which is superclass of both classes provided, or null if the first common superclass is java.lang.Object] */ public Class findCommonAncestor (Class cl1, Class cl2) { // handle direct inheritance if (cl1.isAssignableFrom (cl2)) return cl1; // cl2 is subclass of cl1 if (cl2.isAssignableFrom (cl1)) return cl2; // cl1 is subclass of cl2 ArrayList cl1Ancestors = new ArrayList (8); ArrayList cl2Ancestors = new ArrayList (8); Class cl1An = cl1.getSuperclass (); Class cl2An = cl2.getSuperclass (); while (cl1An != null) { cl1Ancestors.add (cl1An); cl1An = cl1An.getSuperclass (); } while (cl2An != null) { cl2Ancestors.add (cl2An); cl2An = cl2An.getSuperclass (); } if (cl2Ancestors.size () > cl1Ancestors.size ()) { ArrayList temp = cl1Ancestors; cl1Ancestors = cl2Ancestors; cl2Ancestors = temp; } // cl1Ancestors is now the longer stack of classes, // i.e. it must contain the first common superclass Class foundClass = null; for (Iterator it = cl1Ancestors.iterator (); it.hasNext ();) { Object o = it.next (); if (cl2Ancestors.contains (o)) { foundClass = (Class)o; break; } } if (foundClass.equals (Object.class)) { foundClass = null; // if Object is the first common superclass, null is returned } return foundClass; } /** A utility method which looks for the first common ancestor of the * classes specified. The ancestor is either one of the two classes, if one of them extends the other * or their first superclass which is common to both. * The stopClass parameter can be used to limit the superclass to be found to be instance * @param cl1 the first class * @param cl2 the first class * @param stopClass a class to limit the results to, i.e. a result returned is either subclass of the stopClass or null. * If the stopClass is null, the result is not limited to any particular class * @return class which is superclass of both classes provided, or null if the first common superclass is not inmstance of stopClass] */ public Class findCommonAncestor (Class cl1, Class cl2, Class stopClass) { Class cl = findCommonAncestor (cl1, cl2); if (stopClass == null) return cl; if ((cl == null) || (!(stopClass.isAssignableFrom (cl)))) return null; return cl; } // ----------------------------------------------------------------------------- // XML utilities /** Read property from XML node. * @param propName name of property * @param propClass class of property (to find editor) * @param element XML element representing the property * @return value of property created from XML element */ public static Object readProperty (String propName, Class propClass, org.w3c.dom.Node element) throws java.io.IOException { org.w3c.dom.NodeList items = element.getChildNodes (); Object result = null; if (items.getLength () >0) { PropertyEditor propEdit = FormPropertyEditorManager.findEditor (propClass); for(int i=0, n=items.getLength ();i<n; i++){ if (items.item (i).getNodeType () == org.w3c.dom.Node.ELEMENT_NODE && ((org.w3c.dom.Element) items.item (i)).getAttribute (PROP_NAME).equals (propName)) { ((XMLPropertyEditor) propEdit).readFromXML (items.item (i)); result = propEdit.getValue(); } } } if (result == null) { org.w3c.dom.NamedNodeMap attributes = element.getAttributes (); if (attributes != null) { org.w3c.dom.Node attr = attributes.getNamedItem (propName); if (attr!=null) { String valueText = attr.getNodeValue (); if (valueText != null ){ result = GandalfPersistenceManager.decodeValue (valueText); } } } } return result; } /** Write information about Color into XML element. * @param propName name of property * @param value value of property * @param propClass class of property (to find editor) * @param element XML element to write to * @param doc the whole XML document */ public static void writeProperty (String propName, Object value, Class propClass, org.w3c.dom.Element el,org.w3c.dom.Document doc) { boolean written = false; PropertyEditor propEdit = FormPropertyEditorManager.findEditor (propClass); org.w3c.dom.Node valueNode = null; if (propEdit instanceof XMLPropertyEditor) { propEdit.setValue (value); valueNode = ((XMLPropertyEditor) propEdit).storeToXML (doc); if (valueNode != null) { el.appendChild (valueNode); if (valueNode.getNodeType () == org.w3c.dom.Node.ELEMENT_NODE) { ((org.w3c.dom.Element) valueNode).setAttribute (PROP_NAME, propName); } written = true; } } if (!written) { String encodedSerializeValue = GandalfPersistenceManager.encodeValue (value); if (encodedSerializeValue != null) { el.setAttribute (propName, encodedSerializeValue); } else { // [PENDING - notify problem?] } } } } /* * Log * 26 Gandalf 1.25 1/18/00 Pavel Buzek ignoring some properties * in copy/paste * 25 Gandalf 1.24 1/13/00 Ian Formanek NOI18N #2 * 24 Gandalf 1.23 1/5/00 Ian Formanek NOI18N * 23 Gandalf 1.22 12/13/99 Pavel Buzek * 22 Gandalf 1.21 12/9/99 Pavel Buzek utils for XML properties * - readProperty, writeProperty * 21 Gandalf 1.20 11/25/99 Ian Formanek Uses Utilities module * 20 Gandalf 1.19 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 19 Gandalf 1.18 10/5/99 Ian Formanek cloneObject added * 18 Gandalf 1.17 9/17/99 Ian Formanek Removed obsoleted code, * findCommonAncestor method added * 17 Gandalf 1.16 7/27/99 Ian Formanek getAdapterForListener -> * BeanSupport * 16 Gandalf 1.15 7/19/99 Ian Formanek pack () in test mode * 15 Gandalf 1.14 7/4/99 Ian Formanek aded menuBar ignored * property to JInternalFrame * 14 Gandalf 1.13 6/27/99 Ian Formanek method * getMethodHeaderText moved to JavaCodeGenerator * 13 Gandalf 1.12 6/9/99 Ian Formanek ---- Package Change To * org.openide ---- * 12 Gandalf 1.11 5/16/99 Ian Formanek * 11 Gandalf 1.10 5/15/99 Ian Formanek * 10 Gandalf 1.9 5/15/99 Ian Formanek * 9 Gandalf 1.8 5/13/99 Ian Formanek * 8 Gandalf 1.7 5/10/99 Ian Formanek * 7 Gandalf 1.6 5/4/99 Ian Formanek Package change * 6 Gandalf 1.5 4/29/99 Ian Formanek * 5 Gandalf 1.4 4/7/99 Ian Formanek Debug finalized, * Hashtable->HashMap * 4 Gandalf 1.3 3/29/99 Ian Formanek Added DEBUG methods * 3 Gandalf 1.2 3/28/99 Ian Formanek * 2 Gandalf 1.1 3/24/99 Ian Formanek * 1 Gandalf 1.0 3/17/99 Ian Formanek * $ */